package thread_case;

class MyBlockingQueue {
    // 创建 String 数组模拟队列
    private String[] elems = null;
    // head 记录队列的头
    private int head = 0;
    // tail 记录队列的尾
    private int tail = 0;
    // size 记录当前队列中元素个数
    private int size = 0;
    // locker 作为锁对象,进行加锁操作时使用
    private Object locker = new Object();
    // 构造方法,创建出一个长度为 len 的队列
    public MyBlockingQueue(int len) {
        elems = new String[len];
    }
    // 向队列中添加元素 elem
    public void put(String elem) throws InterruptedException {
        // 锁加这里与加到方法上的本质是一样的,加到方法上是给 this 加锁,此处是给 locker 加锁
        synchronized (locker) {
            while (size >= elems.length) {
                // 队列满了,需要阻塞等待其他线程对这个队列进行出队列操作唤醒此处 wait
                locker.wait();
            }
            // 新的元素放到 tail 指向的位置
            elems[tail] = elem;
            tail++;
            if (tail >= elems.length) {
                // 下标超过队列长度就回到 0 号下标位置
                tail = 0;
            }
            // 入队列成功,队列元素个数 +1
            size++;
            // 入队列成功后唤醒
            locker.notify();
        }
    }
    // 从队列中取出元素,并返回
    public String take() throws InterruptedException {
        String ret = null;
        synchronized (locker) {
            while (size == 0) {
                // 队列为空,需要阻塞等待其他线程对这个队列进行入队列操作,唤醒此处的 wait
                locker.wait();
            }
            // 取出 head 位置的元素
            ret = elems[head];
            head++;
            if (head >= elems.length) {
                // 下标超过队列长度就回到 0 号下标位置
                head = 0;
            }
            // 出队列成功,队列中元素个数 -1
            size--;
            // 元素出队列成功后唤醒
            locker.notify();
        }
        // 返回取出的元素值
        return ret;
    }
}

public class ThreadDemo5 {
    public static void main(String[] args) throws InterruptedException {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(100);
        Thread customer = new Thread(()->{
            while (true) {
                try {
                    String elem = myBlockingQueue.take();
                    System.out.println("消费元素:-> " + elem);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"消费者");
        Thread producer = new Thread(()->{
            int n = 0;
            while (true) {
                try {
                    myBlockingQueue.put(n + "");
                    System.out.println("生产元素:-> " + n);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                n++;
            }
        },"生产者");
        // 启动生产者与消费者线程
        customer.start();
        producer.start();
    }
}
